package com.hk.huakangcompany.utils;

import com.alibaba.fastjson.JSON;
import org.elasticsearch.action.admin.indices.delete.DeleteIndexRequest;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.delete.DeleteResponse;
import org.elasticsearch.action.get.GetRequest;
import org.elasticsearch.action.get.GetResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.index.IndexResponse;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.action.support.master.AcknowledgedResponse;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.client.indices.CreateIndexRequest;
import org.elasticsearch.client.indices.CreateIndexResponse;
import org.elasticsearch.client.indices.GetIndexRequest;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.text.Text;
import org.elasticsearch.common.unit.TimeValue;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.MatchAllQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.rest.RestStatus;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * @author zhe
 * @Create 2021/8/1/8:54
 * @State Es 操作工具类
 */
@Component
public class EsUtil {

    public static final String ES_INDEX = "hk_loginlog"; //索引库
//    public static final String ES_INDEX = "hk_company"; //索引库
    public static final String ES_SHARDS = "5"; //分片
    public static final String ES_REPLICAS = "1"; // 副本

    @Autowired
    @Qualifier("restHighLevelClient")
    private RestHighLevelClient client;

    /**
     * 创建索引
     * @param index 索引库
     * @param shards 分片
     * @param replicas 副本
     * @return 是否创建
     * @throws IOException
     */
    public boolean createIndex(String index,String shards,String replicas) throws IOException {
//        创建索引对象
        CreateIndexRequest request = new CreateIndexRequest(index);
        request.settings(Settings.builder()
                // 设置分片数为5， 副本为1
                .put("index.number_of_shards", shards)
                .put("index.number_of_replicas", replicas)
        );
//        2、客户端执行请求 操作索引indices()
        CreateIndexResponse createIndexResponse =
                client.indices().create(request, RequestOptions.DEFAULT);
//        System.out.println("索引是否创建"+createIndexResponse.isAcknowledged());
        return createIndexResponse.isAcknowledged();
    }

    /**
     * 判断索引
     * @param index 索引库
     * @return 索引是否存在
     * @throws IOException
     */
    public boolean isIndexExists(String index) throws IOException {
        GetIndexRequest getIndexRequest = new GetIndexRequest(index);
        boolean exists = client.indices().exists(getIndexRequest,RequestOptions.DEFAULT);
//        System.out.println("当前索引是否存在："+exists);
        return exists;
    }

    /**
     * 删除索引
     * @param index 索引库
     * @param requestTime 请求时长（秒）
     * @return 是否删除
     * @throws IOException
     */
    public boolean deleteIndex(String index,long requestTime) throws IOException {
        DeleteIndexRequest request = new DeleteIndexRequest(index);
        request.timeout(TimeValue.timeValueSeconds(requestTime));
        AcknowledgedResponse delete = client.indices().delete(request, RequestOptions.DEFAULT);
//        System.out.println("当前索引是否删除："+delete.isAcknowledged());
        return delete.isAcknowledged();
    }


    /**
     * 创建文档
     * @param index 索引库
     * @param o 文档对象
     * @param _id 唯一标识（_id）
     * @param requestTime 请求时长（秒）
     * @return 状态
     * @throws IOException
     */
    public RestStatus createDocument(String index, Object o, String _id, long requestTime) throws IOException {
//        创建请求
        IndexRequest request = new IndexRequest(index);
        request.id(_id);
//        创建请求，超过1秒不执行
        request.timeout(TimeValue.timeValueSeconds(requestTime));
//        将数据放入请求json
        request.source(JSON.toJSONString(o), XContentType.JSON);
//        客户端发送请求,获取响应的结果
        IndexResponse indexResponse = client.index(request, RequestOptions.DEFAULT);
//        System.out.println(indexResponse.toString());
//        System.out.println(indexResponse.status()); //对应RestFul返回状态CREATED
        return indexResponse.status();
    }

    /**
     * 获取文档，判断是否存在
     * @param index 索引库
     * @param _id 唯一标识（_id）
     * @return 文档是否存在
     * @throws IOException
     */
    public boolean isDocumentExists(String index,String _id) throws IOException {
        GetRequest getRequest = new GetRequest(index, _id);
//        不获取返回的_soource 的上下文
        getRequest.fetchSourceContext(new FetchSourceContext(false));
        getRequest.storedFields("_none_");
//        需要哪些字段都可以获取
//        getRequest.storedFields("");
        boolean exists = client.exists(getRequest,RequestOptions.DEFAULT);
//        System.out.println("是否存在"+exists);
        return exists;
    }

    /**
     * 获取文档信息
     * @param index 索引库
     * @param _id 唯一标识（_id）
     * @return 返回文档String（JSON）
     * @throws IOException
     */
    public String getDocumentInfo(String index,String _id) throws IOException {
        GetRequest getRequest = new GetRequest(index,_id);
        GetResponse getResponse = client.get(getRequest, RequestOptions.DEFAULT);
//        System.out.println(getResponse.getSourceAsString());
        return getResponse.getSourceAsString();
    }

    /**
     * 更新文档信息
     * @param index 索引库
     * @param _id 唯一标识（_id）
     * @param o 文档对象
     * @param requestTime 请求时长（秒）
     * @return 修改状态
     * @throws IOException
     */
    public RestStatus updateDocumentInfo(String index,String _id,Object o,long requestTime) throws IOException {
        UpdateRequest updateRequest = new UpdateRequest(index,_id);
        updateRequest.timeout(TimeValue.timeValueSeconds(requestTime));
        updateRequest.doc(JSON.toJSONString(o),XContentType.JSON);
        UpdateResponse updateResponse = client.update(updateRequest, RequestOptions.DEFAULT);
//        System.out.println("是否修改："+updateResponse.status());
        return updateResponse.status();
    }

    /**
     * 删除文档信息
     * @param index 索引库
     * @param _id 唯一标识（_id）
     * @param requestTime 请求时长（秒）
     * @return 删除状态
     * @throws IOException
     */
    public RestStatus deleteDocumentInfo(String index,String _id,long requestTime) throws IOException {
        DeleteRequest deleteRequest = new DeleteRequest(index,_id);
        deleteRequest.timeout(TimeValue.timeValueSeconds(requestTime));
        DeleteResponse deleteResponse = client.delete(deleteRequest, RequestOptions.DEFAULT);
//        System.out.println("是否删除："+deleteResponse.status());
        return deleteResponse.status();
    }

    /**
     * 批量操作示例
     */
    /*{
        List<LoginLog> loginLogList = new ArrayList<>();
        for (int i = 1; i < 10 ; i++) {
            //        创建对象
            LoginLog loginLog = new LoginLog(i,i,"张三"+i,i+"22121@qq.com","192.168.2."+i,"2021-2-"+i,"2021-2-"+i,0);
            loginLogList.add(loginLog);
        }
//        批处理请求
        for (int i = 0; i < loginLogList.size(); i++) {
//            批量修改和删除也是add(构造参数不一样)
            bulkRequest.add(
                    new IndexRequest("hk_company")
                            .id(""+(i+1))
                            .source(JSON.toJSONString(loginLogList.get(i)),XContentType.JSON));
        }
    }*/

    /**
     * 批量插入文档数据
     * @param requestTime 请求时长（秒）
     * @param list 批量插入请求文档数据
     * @return 是否执行失败 true:执行失败（未插入）
     * @throws IOException
     */
    public boolean bulkCreateDocument(long requestTime,List<IndexRequest> list) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout(TimeValue.timeValueSeconds(requestTime));
//        批处理请求
        for (int i = 0; i < list.size(); i++) {
//            批量修改和删除也是add(构造参数不一样)
            bulkRequest.add(list.get(i));
        }
//        执行请求
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
//        System.out.println("请求是否执行完成："+bulkResponse.status());
//        System.out.println("请求是否执行是否失败："+bulkResponse.hasFailures());
        return bulkResponse.hasFailures();
    }

    /**
     * 批量更新文档数据
     * @param requestTime 请求时长（秒）
     * @param list 批量更新请求文档数据
     * @return 是否执行失败 true:执行失败（未插入）
     * @throws IOException
     */
    public boolean bulkUpdateDocument(long requestTime,List<UpdateRequest> list) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout(TimeValue.timeValueSeconds(requestTime));
//        批处理请求
        for (int i = 0; i < list.size(); i++) {
//            批量修改和删除也是add(构造参数不一样)
            bulkRequest.add(list.get(i));
        }
//        执行请求
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
//        System.out.println("请求是否执行完成："+bulkResponse.status());
//        System.out.println("请求是否执行是否失败："+bulkResponse.hasFailures());
        return bulkResponse.hasFailures();
    }

    /**
     * 批量删除文档数据
     * @param requestTime 请求时长（秒）
     * @param list 批量删除请求文档数据
     * @return 是否执行失败 true:执行失败（未插入）
     * @throws IOException
     */
    public boolean bulkDeleteDocument(long requestTime, List<DeleteRequest> list) throws IOException {
        BulkRequest bulkRequest = new BulkRequest();
        bulkRequest.timeout(TimeValue.timeValueSeconds(requestTime));
//        批处理请求
        for (int i = 0; i < list.size(); i++) {
//            批量修改和删除也是add(构造参数不一样)
            bulkRequest.add(list.get(i));
        }
//        执行请求
        BulkResponse bulkResponse = client.bulk(bulkRequest, RequestOptions.DEFAULT);
//        System.out.println("请求是否执行完成："+bulkResponse.status());
//        System.out.println("请求是否执行是否失败："+bulkResponse.hasFailures());
        return bulkResponse.hasFailures();
    }

    /**
     * 查询所有
     * @param index 索引库
     * @param requestTime 请求时长（秒）
     * @param pageNo 当前页从0开始 公式（请求页-1）*每页条数
     * @param pageSize 每页条数
     * @return resultMap key：total(name),value:Long(type) ，key：list(name),value:list<map>(type)
     * @throws IOException
     */
    public Map<String,Object> searchDocumentAll(String index, long requestTime, int pageNo, int pageSize) throws IOException {
        if (pageNo <= 1) {
            pageNo = 1;
        }
//        创建搜索请求
        SearchRequest searchRequest = new SearchRequest(index);
//        构造搜索条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //        排序 降序
        sourceBuilder.sort("id", SortOrder.DESC);
//        查询条件，我们可以使用 QueryBuilders工具来实现
        MatchAllQueryBuilder queryBuilder = QueryBuilders.matchAllQuery();
        sourceBuilder.query(queryBuilder);
//        分页 具有默认值
        sourceBuilder.from(pageNo);
        sourceBuilder.size(pageSize);
//        高并发请求时长
        sourceBuilder.timeout(new TimeValue(requestTime, TimeUnit.SECONDS));
//        将搜索条件放入搜索请求
        searchRequest.source(sourceBuilder);
//        执行搜索
        SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
        //        将总结果存入map集合
        Map<String,Object> resultMap = new HashMap<>(2);
//        总条数
        long total = search.getHits().getTotalHits().value;
//        System.out.println("搜索的条数:"+total);
        resultMap.put("total",total);
//        System.out.println("搜索的数据:"+JSON.toJSONString(search.getHits().getHits()));
        SearchHit[] hits = search.getHits().getHits();
//          将查询结果存入List集合
        List<Object> list = new ArrayList<>(100);
        for (SearchHit hit : hits) {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
            list.add(sourceAsMap);
//            System.out.println("搜索的数据"+sourceAsMap);
        }
        resultMap.put("list",list);
        return resultMap;
    }

    /**
     *
     * 高亮多条件查询
     * @param index 索引库
     * @param requestTime 请求时长（秒）
     * @param pageNo 当前页从0开始  公式（请求页-1）*每页条数
     * @param pageSize 每页条数
     * @param map 搜索条件 map的key必须是value的变量名称，否则查询不到结果
     * @return resultMap key：total(name),value:Long(type) ，key：list(name),value:list<map>(type)
     * @throws IOException
     */
    public Map<String,Object> searchDocument(String index, long requestTime,int pageNo, int pageSize, Map<String,Object> map) throws IOException {
//        创建搜索请求
        SearchRequest searchRequest = new SearchRequest(index);
//        构造搜索条件
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        //        排序 降序 先显示最新的情况
        sourceBuilder.sort("id", SortOrder.DESC);
//        查询条件，我们可以使用 QueryBuilders工具来实现
//        MatchQueryBuilder queryBuilder = QueryBuilders.matchQuery("loginName", "张三");
        //        构建高亮对象
        HighlightBuilder highlightBuilder = new HighlightBuilder();
//        多条件查询
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
//        获取所有的key，通过key获取对应value，并查看是否有值 高亮字段必须和搜索结果字段一致key
        Set<String> keySet = map.keySet();
        for (String key : keySet) {
            if (map.get(key) != null && map.get(key) != "") {
                boolQueryBuilder.must(QueryBuilders.matchQuery(key,map.get(key)));
                //        设置高亮域
                highlightBuilder.field(key);
            }
        }
        //        是否多个高亮显示
        highlightBuilder.requireFieldMatch(false);
//        高亮前后缀
        highlightBuilder.preTags("<span style='color:red'>");
        highlightBuilder.postTags("</span>");
//        将多条查询条件放入搜索条件中
        sourceBuilder.query(boolQueryBuilder);
//        将高亮放入搜索条件中
        sourceBuilder.highlighter(highlightBuilder);
//        分页 具有默认值
        sourceBuilder.from(pageNo);
        sourceBuilder.size(pageSize);
//        高并发请求时长
        sourceBuilder.timeout(new TimeValue(requestTime, TimeUnit.SECONDS));
//        将搜索条件放入搜索请求
        searchRequest.source(sourceBuilder);
//        执行搜索请求
        SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
        //        将总结果存入map集合
        Map<String,Object> resultMap = new HashMap<>(2);
        //        总条数
        long total = search.getHits().getTotalHits().value;
        resultMap.put("total",total);
//        System.out.println("搜索的条数:"+total);
//        System.out.println("搜索的数据:"+JSON.toJSONString(search.getHits().getHits()));
        SearchHit[] hits = search.getHits().getHits();
        //        存入List集合
        List<Map<String,Object>> list = new ArrayList<>(30);
        for (SearchHit hit : hits) {
            Map<String, Object> sourceAsMap = hit.getSourceAsMap();
//            获取高亮字段
            Map<String, HighlightField> highlightFields = hit.getHighlightFields();
//            for遍历key
            for (String key : keySet) {
                if (map.get(key) != null && map.get(key) != "") {
//                    获取高亮字段
                    HighlightField value = highlightFields.get(key);
                    //     解析高亮字段 将原来的字段换成高亮的即可
                    if (value != null) {
                        Text[] fragments = value.fragments();
                        String n_value = "";
                        for (Text text : fragments) {
                            n_value +=text;
                        }
                        sourceAsMap.put(key,n_value);
                    }
                }
            }
            list.add(sourceAsMap);
        }
        resultMap.put("list",list);
        return resultMap;
    }



}
